/***********************************
* Name: ToolbarFrame.java
* Date: Aug 7, 2010
* @author martin
* @author karen
* Description:
***********************************/

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JToolBar;
import javax.swing.filechooser.FileNameExtensionFilter;
//**************************************
//to parse an XML file
import java.io.*;
import java.util.*;
import org.jdom.*;
import org.jdom.input.*;
import org.jdom.output.*;
//**************************************


public class ToolbarFrame extends JFrame {

    protected   JToolBar  toolBar;
    private     MainPanel panel;
    private     LinkedList<Gate> gates = new LinkedList<Gate>();
    private     String current_file_path = "";
    private     LinkedList<Route> routes = new LinkedList<Route>();

    /*****************************************
    * ToolbarFrame
    * Constructor
    *****************************************/
    public ToolbarFrame() {
        super("Combinatorial Timing Analysis");
        this.setSize(800, 600);

        JMenuBar menuBar = createMenuBar();
        this.setJMenuBar(menuBar);

        this.toolBar.setOrientation(JToolBar.HORIZONTAL);
        //this.setLayout(new GridLayout(2,1));
        //this.getContentPane().add(toolBar);
        this.getContentPane().add(toolBar, BorderLayout.NORTH);
        
        //Add the Main Panel
        panel = new MainPanel();
        
        this.getContentPane().add(panel);
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    /*****************************************
    * createMenuBar
    * @return JMenuBar with all the File and Help bar
    * creates all the bars
    *****************************************/
    protected JMenuBar createMenuBar(){
        final JMenuBar menuBar = new JMenuBar();

        //************************ Actions ************************
        Action actionNew = new AbstractAction("New", Main.iconNew) {
          public void actionPerformed(ActionEvent e) {
            newFile();
          }
        };
        Action actionOpen = new AbstractAction("Open...", Main.iconOpen) {
            public void actionPerformed(ActionEvent e) {
                openFile();
            }
        };
        Action actionSave = new AbstractAction("Save...", Main.iconSave) {
            public void actionPerformed(ActionEvent e) {
                saveFile();
            }
        };
        Action actionSaveAs = new AbstractAction("Save As...", Main.iconSaveAs) {
            public void actionPerformed(ActionEvent e) {
                saveAsFile();
            }
        };
        Action actionExit = new AbstractAction("Exit") {
          public void actionPerformed(ActionEvent e) {
            exit();
          }
        };
        Action actionAnalyze = new AbstractAction("Analyze", Main.iconAnalyze) {
          public void actionPerformed(ActionEvent e) {
            analyze();
          }
        };
        //*********************************************************

        //************************* JMenu *************************
        JMenu mFile = new JMenu("File");
        mFile.setMnemonic('F');
        JMenuItem item = mFile.add(actionNew);
            mFile.add(item);
        //-----------------------------
        item = mFile.add(actionOpen);
            mFile.add(item);
        //-----------------------------
        item = mFile.add(actionSave);
            mFile.add(item);
        //-----------------------------
        item = mFile.add(actionSaveAs);
            mFile.add(item);
        //-----------------------------
        mFile.addSeparator();
        item = mFile.add(actionExit);
            item.setMnemonic('x');
        //*****************************
        JMenu mHelp = new JMenu("Help");
        mHelp.setMnemonic('H');

        mHelp.addSeparator();
        //*****************************
        JMenu mAnalyze = new JMenu("Analize");
        mAnalyze.setMnemonic('A');
        item = mAnalyze.add(actionAnalyze);
            mAnalyze.add(item);
        //-----------------------------


        mHelp.addSeparator();

        menuBar.add(mFile);
        menuBar.add(mAnalyze);
        menuBar.add(mHelp);
        //-----------------------------
        //*********************************************************
        //*********************** JToolBar ************************
        toolBar = new JToolBar();
        JButton btn1 = toolBar.add(actionNew);
        btn1.setToolTipText("New file");
        JButton btn2 = toolBar.add(actionOpen);
        btn2.setToolTipText("Open file");
        JButton btn3 = toolBar.add(actionSave);
        btn3.setToolTipText("Save file");
        JButton btn4 = toolBar.add(actionSaveAs);
        btn4.setToolTipText("Save file");
        toolBar.addSeparator();
        JButton btn5 = toolBar.add(actionAnalyze);
        btn5.setToolTipText("Analyze");
        
        //*********************************************************
        return menuBar;
    }

    /*****************************************
     * exit
     * @return void
     * leave the program
     *****************************************/
    private void exit() {
        //TODO falta hacer que pregunte si quiere guardar
        System.out.println("exit");
        System.exit(0);
   }

    /*****************************************
     * newFile
     * @return void
     * creates an empty file
     *****************************************/
    private void newFile() {
        //TODO falta hacer que pregunte si quiere guardar
        System.out.println("new file");
   }

    /*****************************************
     * openFile
     * @return void
     * open a file
     *****************************************/
    private void openFile() {
        System.out.println("open file");

        JFileChooser jfc = new JFileChooser();
        jfc.setFileFilter(new FileNameExtensionFilter("XML document", "xml"));
        int result = jfc.showOpenDialog(this);
        if(result == JFileChooser.CANCEL_OPTION)
            return;
        try{
            File selectedFile = jfc.getSelectedFile();
            boolean acepted = this.loadXML(selectedFile);
            if(acepted){
                this.panel.setGates(this.gates);
                this.panel.getPanel_center().repaint();
            }
            
        } catch (Exception exp) {
            JOptionPane.showMessageDialog(this,"File loading error:\n"+exp.getMessage(),"File loading error",JOptionPane.ERROR_MESSAGE);
        }
    }

    /*****************************************
    * saveFile
    * @return void
    * save a file
    *****************************************/
    private void saveFile( ) {
        if(this.current_file_path.isEmpty()){
            this.saveAsFile();
        }
        else{
            System.out.println("save file - "+this.current_file_path);

            File current_file = new File(this.current_file_path);
            try{
                BufferedWriter bw = new BufferedWriter(new FileWriter(current_file));
                bw.write(this.getFullXML());
                bw.close();
            }
            catch (Exception exp) {
                JOptionPane.showMessageDialog(this,"An error has ocurred while saving:\n"+exp.getMessage(),"Saving Error",JOptionPane.ERROR_MESSAGE);
                this.current_file_path = "";
            }
        }
   }

   /*****************************************
    * saveAsFile
    * @return void
    * leave the program
    *****************************************/
    private void saveAsFile() {
        System.out.println("save As File");
        String rescue = this.current_file_path;
        JFileChooser jfc = new JFileChooser();
        jfc.setFileFilter(new FileNameExtensionFilter("XML document", "xml"));
        int result = jfc.showSaveDialog(this);
        
        if(result == JFileChooser.CANCEL_OPTION){
            return;
        }
        
        File current_file = jfc.getSelectedFile();
        this.current_file_path = current_file.getAbsolutePath();
        
        System.out.println("File: "+current_file_path);

        try {
           BufferedWriter bw = new BufferedWriter(new FileWriter(current_file));
           bw.write(this.getFullXML());
           bw.close();
        }
        catch (Exception exp) {
           JOptionPane.showMessageDialog(this,"An error has ocurred while saving:\n"+exp.getMessage(),"Saving Error",JOptionPane.ERROR_MESSAGE);
           this.current_file_path = rescue;
        }
    }

    /*****************************************
    * obtain_all_routes
    * @return void
    * Obtains a list of routes
    *****************************************/
    private void obtain_all_routes(Gate first_gate) {

        System.out.println("Obtain a list of routes");
        

        if(first_gate.getType() == Gate.NOT){
            obtain_route(panel.getGateHashValue(first_gate.getName()).getA().getFrom() , first_gate.getName()+"_A");
            return ;
        }
        //with input A
        obtain_route(panel.getGateHashValue(first_gate.getName()).getA().getFrom() , first_gate.getName()+"_A");
        //with input B
        obtain_route(panel.getGateHashValue(first_gate.getName()).getB().getFrom() , first_gate.getName()+"_B");

        
        for(Route a : this.routes){
            System.out.println("Route: "+a);
        }

    }

    /*****************************************
    * obtain_all_routes
    * @return void
    * Obtains a list of routes
    *****************************************/
    private void obtain_route(String gate_name, String str){

        if(gate_name.startsWith("E")){
            //ends the recursion

            this.routes.add(new Route(str+"-"+gate_name));
            return;
        }

        //Obtain the gate
        Gate gate = panel.getGateHashValue(gate_name);

        String str2 = str;

        if(gate.getType() == Gate.NOT){
            //Just one
            //get input A and get from
            str2 = str+"-"+gate.getName()+"_A";
            obtain_route(gate.getA().getFrom(),str2);
            return;
        }

        //get input A
        str2 = str+"-"+gate.getName()+"_A";
        obtain_route(gate.getA().getFrom(),str2);
        //get input B
        str2 = str+"-"+gate.getName()+"_B";
        obtain_route(gate.getB().getFrom(),str2);
        return;
    }

    /*****************************************
    * analyze
    * @return void
    * analyze
    *****************************************/
    private void analyze() {
        
        System.out.println("analyze");

        this.routes.clear();
        int count_outputs = 0;
        this.gates = panel.getGates();
        Gate first_gate = null;
        for(Gate a : this.gates){
            if(a.isOutput()){
                first_gate = a;
                count_outputs++;
            }
        }

        if(first_gate == null){
            System.out.println("No gate has output");
            return;
        }

        if(count_outputs!=1 && count_outputs!=0){
            JOptionPane.showMessageDialog(this,"Inappropriate output quantity:\n"+count_outputs+" found, must be one output","Inappropriate output",JOptionPane.ERROR_MESSAGE);
            return;
        }

        obtain_all_routes(first_gate);

        for(Route route : this.routes){
            route.setFalseRoute(this.isFalseRoute(route));
        }

    }

    /*****************************************
     * isFalseRoute
     * @param route to analyze
     * @return boolean with tru if is a false route
     *****************************************/
    private boolean isFalseRoute(Route route) {

        //sensitize
        sensitize(route);
        //justify

        return false;
    }

    /*****************************************
     * sensitize
     * @return String with the type string
     *****************************************/
    private void sensitize(Route route) {

        //obtain the order
        LinkedList<String> order_gates = route.getGates_in_order_from_input();

        String entry = order_gates.getFirst();
        System.out.println("ToolbarFrame.sensitize: route: "+route.toString());
        //from 1 because the first one is the entry
        for(int i=1;i<order_gates.size();i++){
            String gate_name = order_gates.get(i).substring(0, order_gates.get(i).length()-2);
            char input = order_gates.get(i).charAt(order_gates.get(i).length()-1);
            Gate gate = panel.getGateHashValue(gate_name);
            gate.resetAnalyze();
            gate.sensitize(input);
            System.out.println("ToolbarFrame.sensitize: \tGate Name: "+gate.getName()+"  "+gate.getSensitize());
        }


    }

    /*****************************************
     * getTrueFalse
     * @return String with the type string
     *****************************************
    public boolean getTrueFalse(String type){
        if(type.compareToIgnoreCase("true")==0)
            return true;
        return false;
    }
     */

    /*****************************************
     * loadXML
     * @return void
     * load an XML file
     *****************************************/
    private boolean loadXML(File file) {
        System.out.println("loading the XML file... "+file.getName());
        this.gates.clear();
        try {
            SAXBuilder builder = new SAXBuilder(false);
            Document doc = builder.build(file);
            Element root = doc.getRootElement();

            List<Element> list = root.getChildren("Gate");
            for(int i=0 ; i< list.size() ; i++){
                Element element = list.get(i);
                String name = "";
                int type = -1;
                //boolean end = false;
                int x = -1;
                int y = -1;
                // --- name ------------
                name = element.getAttributeValue("name");
                int num = Gate.getTypeGateInt(element.getAttributeValue("type"));
                if(num==-1){
                    JOptionPane.showMessageDialog(this,"Error while parsing:\n"+"Gate type not found","Error while parsing the XML",JOptionPane.ERROR_MESSAGE);
                    return false;
                }
                // --- type ------------
                type = num;
                // --- end -------------
                //end = getTrueFalse(element.getAttributeValue("end"));

                Element pos = element.getChild("position");
                // --- x ---------------
                x = Integer.parseInt(pos.getAttributeValue("x"));
                // --- y ---------------
                y = Integer.parseInt(pos.getAttributeValue("y"));

                // --- Input A ---------
                Input A = null;
                // --- Input B ---------
                Input B = null;

                List<Element> input_list = element.getChildren("input");
                if(type == Gate.NOT && input_list.size()==1){
                    //is a NOT gate (has just one input)
                    int fallingDelay = Integer.parseInt(input_list.get(0).getAttributeValue("fallingDelay"));
                    int raisingDelay = Integer.parseInt(input_list.get(0).getAttributeValue("raisingDelay"));
                    String from = input_list.get(0).getAttributeValue("from");
                    A = new Input(fallingDelay, raisingDelay, from);
                    B = new Input();
                }
                else if(input_list.size()==2){
                    //other gate (has 2 inputs)
                    int fallingDelay_A = Integer.parseInt(input_list.get(0).getAttributeValue("fallingDelay"));
                    int raisingDelay_A = Integer.parseInt(input_list.get(0).getAttributeValue("raisingDelay"));
                    String from_A = input_list.get(0).getAttributeValue("from");
                    A = new Input(fallingDelay_A, raisingDelay_A, from_A);
                    int fallingDelay_B = Integer.parseInt(input_list.get(1).getAttributeValue("fallingDelay"));
                    int raisingDelay_B = Integer.parseInt(input_list.get(1).getAttributeValue("raisingDelay"));
                    String from_B = input_list.get(1).getAttributeValue("from");
                    B = new Input(fallingDelay_B, raisingDelay_B, from_B);
                }
                else{
                    JOptionPane.showMessageDialog(this,"Error while parsing:\n"+"Invalid number of Inputs in "+Gate.getTypeString(type)+" gate","Error while parsing the XML",JOptionPane.ERROR_MESSAGE);
                    return false;
                }

                //Gate newGate = new Gate(name, type, A, B, end, x, y);
                Gate newGate = new Gate(name, type, A, B, x, y);
                this.gates.add(newGate);
                //System.out.println("#############################"+"\n"+"type: "+type+"\n"+"name: "+name+"\n"+"end: "+end+"\n"+"A: "+A+"\n"+"B: "+B+"\n"+"x: "+x+"\n"+"y: "+y+"\n"+"#############################");
            }
            

        }catch (Exception exp){
            JOptionPane.showMessageDialog(this,"Error while parsing:\n"+exp.getMessage(),"Error while parsing the XML",JOptionPane.ERROR_MESSAGE);
            return false;
        }

        return true;
    }
    /*****************************************
     * getFullXML()
     * @return String with all the content of the XML
     *****************************************/
    private String getFullXML() {
        System.out.println("getFullXML");
        String full_xml = "";

        this.gates = this.panel.getGates();

        full_xml = "<circuit>"+"\n\n";

        for(Gate a : this.gates){
            full_xml += a.toString()+"\n\n";
        }

        full_xml += "</circuit>";

        return full_xml;
    }

}